package org.example.platform.elasticsearch.service;

import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.GaussDecayFunctionBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.example.platform.elasticsearch.entity.Hotel;
import org.example.platform.elasticsearch.entity.HotelDocRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
@Slf4j
public class ESQueryDocService {
    @Autowired
    private RestHighLevelClient client;
    @Autowired
    CommonService commonService;

    /**
     * 计数查询
     *
     * @param hotelDocRequest
     * @return
     * @throws IOException
     */
    public long getCityCount(HotelDocRequest hotelDocRequest) throws Exception {
        String indexName = hotelDocRequest.getIndexName();
        commonService.indexNameIsBlank(indexName);
        Hotel hotel = hotelDocRequest.getHotel();
        commonService.hotelIsEmpty(hotel);
        // 客户端的count请求
        CountRequest countRequest = new CountRequest(indexName);
        String city = hotel.getCity();
        // 创建搜索builder
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 创建query
        searchSourceBuilder.query(new TermQueryBuilder("city", city));
        countRequest.source(searchSourceBuilder);
        CountResponse countResponse = client.count(countRequest, RequestOptions.DEFAULT);
        return countResponse.getCount();
    }

    /**
     * 指定返回字段
     *
     * @param hotelDocRequest
     * @return
     */
    public List<Hotel> queryBySource(HotelDocRequest hotelDocRequest) throws Exception {
        String indexName = hotelDocRequest.getIndexName();
        commonService.indexNameIsBlank(indexName);
        Hotel hotel = hotelDocRequest.getHotel();
        commonService.hotelIsEmpty(hotel);
        SearchRequest searchRequest = new SearchRequest(indexName);
        String city = hotel.getCity();
        // 创建搜索builder
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 构建query
        searchSourceBuilder.query(new TermQueryBuilder("city", city));
        // 设定希望返回的字段
        searchSourceBuilder.fetchSource(new String[]{"title", "city"}, null);
        ArrayList<Hotel> list = new ArrayList<>();
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        RestStatus status = searchResponse.status();
        if (status != RestStatus.OK) {
            return Collections.emptyList();
        }

        SearchHits searchHits = searchResponse.getHits();
        for (SearchHit searchHit : searchHits) {
            Hotel h = new Hotel();
            h.setId(searchHit.getId());
            h.setIndex(searchHit.getIndex());   //索引名称
            h.setScore(searchHit.getScore());   //文档得分
            Map<String, Object> dataMap = searchHit.getSourceAsMap();
            h.setTitle((String) dataMap.get("title"));
            h.setCity((String) dataMap.get("city"));
            list.add(h);
        }

        return list;
    }

    /**
     * 分页查询
     *
     * @param hotelDocRequest
     * @return
     */
    public List<Hotel> queryLimit(HotelDocRequest hotelDocRequest) throws Exception {
        String indexName = hotelDocRequest.getIndexName();
        commonService.indexNameIsBlank(indexName);
        Hotel hotel = hotelDocRequest.getHotel();
        commonService.hotelIsEmpty(hotel);
        SearchRequest searchRequest = new SearchRequest(indexName);
        String city = hotel.getCity();
        // 创建搜索builder
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 构建query
        searchSourceBuilder.query(new TermQueryBuilder("city", city));
        // 设定分页参数
        searchSourceBuilder.from(hotelDocRequest.getOffset());
        searchSourceBuilder.size(hotelDocRequest.getLimit());
        // 开启性能分析
        searchSourceBuilder.profile();
        // 设定希望返回的字段
        searchSourceBuilder.fetchSource(new String[]{"title", "city"}, null);
        searchRequest.source(searchSourceBuilder);

        ArrayList<Hotel> list = new ArrayList<>();
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        RestStatus status = searchResponse.status();
        if (status != RestStatus.OK) {
            return Collections.emptyList();
        }

        SearchHits searchHits = searchResponse.getHits();
        for (SearchHit searchHit : searchHits) {
            Hotel h = new Hotel();
            h.setId(searchHit.getId());
            h.setIndex(searchHit.getIndex());   //索引名称
            h.setScore(searchHit.getScore());   //文档得分
            Map<String, Object> dataMap = searchHit.getSourceAsMap();
            h.setTitle((String) dataMap.get("title"));
            h.setCity((String) dataMap.get("city"));
            list.add(h);
        }

        return list;
    }

    /**
     * 查询所有文档
     *
     * @param searchSourceBuilder
     * @return
     * @throws Exception
     */
    public SearchSourceBuilder matchAllQuery(@RequestBody SearchSourceBuilder searchSourceBuilder) {
        // 新建match_all，并设置boost值为2.0
        return searchSourceBuilder.query(new MatchAllQueryBuilder().boost(2.0f));
    }


    /**
     * 日期类型的term查询
     *
     * @param hotelDocRequest
     * @return
     */
    public SearchSourceBuilder getCityByCreateTime(HotelDocRequest hotelDocRequest, SearchSourceBuilder searchSourceBuilder) throws Exception {
        Hotel hotel = hotelDocRequest.getHotel();
        commonService.hotelIsEmpty(hotel);

        String createTimeToSearch = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(hotel.getCreateTime());
        searchSourceBuilder.query(QueryBuilders.termQuery("create_time", createTimeToSearch));
        return searchSourceBuilder;
    }

    /**
     * terms查询
     *
     * @param hotelDocRequest
     * @return
     * @throws Exception
     */
    public SearchSourceBuilder termsQuery(HotelDocRequest hotelDocRequest, SearchSourceBuilder searchSourceBuilder) {
        return searchSourceBuilder.query(QueryBuilders.termQuery("city", hotelDocRequest.getCitys()));
    }

    /**
     * range范围查询
     *
     * @param hotelDocRequest
     * @return
     * @throws Exception
     */
    public SearchSourceBuilder rangeQuery(HotelDocRequest hotelDocRequest, SearchSourceBuilder searchSourceBuilder) {
        Date createStart = hotelDocRequest.getCreateStart();
        String createTimeStart = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(createStart);
        Date createEnd = hotelDocRequest.getCreateEnd();
        String createTimeEnd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(createEnd);

        searchSourceBuilder.query(QueryBuilders.rangeQuery("create_time").gte(createTimeStart).lte(createTimeEnd));
        return searchSourceBuilder;
    }


    /**
     * exists查询
     *
     * @param hotelDocRequest
     * @return
     * @throws Exception
     */
    public SearchSourceBuilder existQuery(HotelDocRequest hotelDocRequest, SearchSourceBuilder searchSourceBuilder) throws Exception {
        String propertiesName = hotelDocRequest.getPropertiesName();
        commonService.propertiesNameIsBlank(propertiesName);

        searchSourceBuilder.from(hotelDocRequest.getOffset());
        searchSourceBuilder.size(hotelDocRequest.getLimit());
        searchSourceBuilder.query(QueryBuilders.existsQuery(propertiesName));
        return searchSourceBuilder;
    }


    /**
     * must查询
     *
     * @param hotelDocRequest
     * @return
     * @throws Exception
     */
    public SearchSourceBuilder mustQuery(HotelDocRequest hotelDocRequest, SearchSourceBuilder searchSourceBuilder) {
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("city", hotelDocRequest.getCitys());

        Date createStart = hotelDocRequest.getCreateStart();
        String createTimeStart = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(createStart);
        Date createEnd = hotelDocRequest.getCreateEnd();
        String createTimeEnd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(createEnd);

        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("create_time").gte(createTimeStart).lte(createTimeEnd);

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(rangeQueryBuilder).must(termQueryBuilder);

        searchSourceBuilder.query(boolQueryBuilder);
        return searchSourceBuilder;
    }


    /**
     * should查询
     *
     * @return
     */
    public SearchSourceBuilder shouldQuery(SearchSourceBuilder searchSourceBuilder) {
        QueryBuilder queryBuilder = QueryBuilders.termQuery("city", "上海");
        QueryBuilder queryBuilder2 = QueryBuilders.termQuery("city", "北京");

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.should(queryBuilder).should(queryBuilder2);

        searchSourceBuilder.query(boolQueryBuilder);
        return searchSourceBuilder;
    }

    /**
     * mustNot查询
     *
     * @return
     */
    public SearchSourceBuilder mustNotQuery(SearchSourceBuilder searchSourceBuilder) {
        QueryBuilder queryBuilder = QueryBuilders.termQuery("city", "上海");
        QueryBuilder queryBuilder2 = QueryBuilders.termQuery("city", "北京");

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.mustNot(queryBuilder).mustNot(queryBuilder2);

        searchSourceBuilder.query(boolQueryBuilder);
        return searchSourceBuilder;
    }


    /**
     * filter查询
     *
     * @return
     */
    public SearchSourceBuilder filterQuery(SearchSourceBuilder searchSourceBuilder) {
        QueryBuilder queryBuilder = QueryBuilders.termQuery("city", "上海");
        QueryBuilder queryBuilder2 = QueryBuilders.termQuery("city", "北京");

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.filter(queryBuilder).filter(queryBuilder2);

        searchSourceBuilder.query(boolQueryBuilder);
        return searchSourceBuilder;
    }

    /**
     * constantScore查询
     *
     * @return
     */
    public SearchSourceBuilder constantScore(SearchSourceBuilder searchSourceBuilder) {
        TermQueryBuilder  queryBuilder = QueryBuilders.termQuery("city", "上海");
        TermQueryBuilder  queryBuilder2 = QueryBuilders.termQuery("city", "北京");

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.should(queryBuilder).should(queryBuilder2);

        ConstantScoreQueryBuilder constantScoreQueryBuilder = new ConstantScoreQueryBuilder(boolQueryBuilder);
        constantScoreQueryBuilder.boost(2.0f);

        searchSourceBuilder.query(constantScoreQueryBuilder);
        return searchSourceBuilder;
    }

    /**
     * functionScoreScore查询
     *
     * @return
     */
    public SearchSourceBuilder functionScoreScore(HotelDocRequest hotelDocRequest, SearchSourceBuilder searchSourceBuilder) {
        TermQueryBuilder  queryBuilder = QueryBuilders.termQuery("city", "上海");

        GaussDecayFunctionBuilder gaussDecayFunctionBuilder = ScoreFunctionBuilders.gaussDecayFunction(hotelDocRequest.getPropertiesName(), 800, 200, 0, 0.2);
        FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(queryBuilder,gaussDecayFunctionBuilder);

        searchSourceBuilder.query(functionScoreQueryBuilder);
        return searchSourceBuilder;
    }


    /**
     * geoBoundingBoxQuery查询
     *
     * @return
     */
    public SearchSourceBuilder geoBoundingBoxQuery(SearchSourceBuilder searchSourceBuilder) {
        // 左上
        GeoPoint top = new GeoPoint(40.187328D, 116.498353D);
        // 右下
        GeoPoint right = new GeoPoint(40.084509D, 116.610461D);
      GeoBoundingBoxQueryBuilder geoBoundingBoxQueryBuilder = new GeoBoundingBoxQueryBuilder("location")
              .setCorners(top,right);

        searchSourceBuilder.query(geoBoundingBoxQueryBuilder);
        return searchSourceBuilder;
    }

    /**
     * geoDistanceQuery查询
     *
     * @return
     */
    public SearchSourceBuilder geoDistanceQuery(SearchSourceBuilder searchSourceBuilder) {
        GeoPoint top = new GeoPoint(40.187328D, 116.498353D);
        // 设定目标距离坐标
        GeoDistanceQueryBuilder geoBoundingBoxQueryBuilder = new GeoDistanceQueryBuilder("location")
                .distance("3", DistanceUnit.KILOMETERS).point(top);

        searchSourceBuilder.query(geoBoundingBoxQueryBuilder);
        return searchSourceBuilder;
    }

    /**
     * geoPolygonQuery查询
     *
     * @return
     */
    public SearchSourceBuilder geoPolygonQuery(SearchSourceBuilder searchSourceBuilder) {
        // 假设目标距离坐标
        ArrayList<GeoPoint> geoPoints = new ArrayList<GeoPoint>();
        GeoPoint sourcePoint1 = new GeoPoint(31.085262D, 121.531257D);
        GeoPoint sourcePoint2 = new GeoPoint(31.085494D, 121.529694D);
        GeoPoint sourcePoint3 = new GeoPoint(31.086252D, 121.530632D);
        geoPoints.add(sourcePoint1);
        geoPoints.add(sourcePoint2);
        geoPoints.add(sourcePoint3);
        GeoPolygonQueryBuilder geoPolygonQueryBuilder = new GeoPolygonQueryBuilder("location", geoPoints);

        searchSourceBuilder.query(geoPolygonQueryBuilder);
        return searchSourceBuilder;
    }



}
